
# DBFtoPDF.py
# Purpose: A program that reads the metadata and data from a .DBF file and
# writes it to a PDF file..

# Author: Vasudev Ram

# Description:

#   Reads the following info from the DBF file:
#   - the file header (as a dictionary), comprising:
#       - the DBF version (signature)
#       - the date of last update of the file
#       - the number of data records in the file
#       - the DBF header length in bytes
#       - the DBF record length in bytes
#       - the number of fields in the DBF file

#   - the field headers (as a list of 4 lists) for each field, comprising:
#       - the field name
#       - the field type
#       - the field length
#       - the field decimals

#   - the data records:
#       - the actual records, either sequentially, from first to last,
#         or, by record number.

# Writes the above data, neatly formatted, to a PDF file.

# $Id: DBFtoPDF.py,v 1.2 2003/03/09 20:46:47 vasudev Exp vasudev $

# $Log: DBFtoPDF.py,v $
# Revision 1.2  2003/03/09 20:46:47  vasudev
# Added RCS Id and Log keywords.
#

##------------------------ imports ---------------------------------------

import os
import sys
import string
import time
from DBFReader import DBFReader
from PDFWriter import PDFWriter

##------------------------ usage ---------------------------------------

def usage():

	sys.stderr.write("Usage: python " + sys.argv[0] + " dbf_file pdf_file\n")
	sys.stderr.write("where dbf_file is a .DBF filename and\n")
	sys.stderr.write("      pdf_file is a .PDF filename.\n")
	sys.stderr.write(sys.argv[0] + " reads dbf_file and writes its\n")
	sys.stderr.write("metadata and data, formatted, to pdf_file.\n")

##------------------------ main ------------------------------------------

def main():

	'''Main program to test DBFReader class.
	'''

	# check for right num. of args
	if (len(sys.argv) != 3):
		usage()
		sys.exit(1)

	# extract dbf and pdf filenames from args
	dbf_fn = sys.argv[1]
	pdf_fn = sys.argv[2]

	# create and open the DBFReader instance
	dr = DBFReader(dbf_fn)
	dr.open()

	# create the PDFWriter instance
	pw = PDFWriter(pdf_fn)

	# and set some of its fields

	# set the font
	pw.setFont("Courier", 10)

	# set the page header
	gen_datetime = time.asctime()
	pw.setHeader("Generated by DBFtoPDF: Input: " + dbf_fn + \
	" At: " + gen_datetime)

	# set the page footer
	pw.setFooter("Generated by DBFtoPDF: Input: " + dbf_fn + \
	" At: " + gen_datetime)

	# create the separator for logical grouping of output
	sep = "=" * 60

	# get the DBF file header
	file_header = dr.read_dbf_file_header()

	# print a separator line
	pw.writeLine(sep)

	# print title for the overall output
	pw.writeLine("Information for DBF file: %s" % (dbf_fn))

	# print a separator line
	pw.writeLine(sep)

	# print the file header section title
	pw.writeLine("")
	pw.writeLine("File Header Information:")

	# print a separator line
	pw.writeLine(sep)

	# setup labels for file header output
	lbl_dbf_ver = \
	"DBF version (signature)       : "
	lbl_last_update = \
	"Date of last update (YY/MM/DD): "
	lbl_num_recs = \
	"Number of data records        : "
	lbl_hdr_len = \
	"DBF header length in bytes    : "
	lbl_rec_len = \
	"DBF record length in bytes    : "
	lbl_num_flds = \
	"Number of fields in DBF file  : "

	# print the file header metadata with labels
	pw.writeLine(lbl_dbf_ver + str(file_header['ver']))
	pw.writeLine(lbl_last_update + file_header['last_update'])
	pw.writeLine(lbl_num_recs + str(file_header['num_recs']))
	pw.writeLine(lbl_hdr_len + str(file_header['hdr_len']))
	pw.writeLine(lbl_rec_len + str(file_header['rec_len']))
	pw.writeLine(lbl_num_flds + str(file_header['num_flds']))

	# print a separator line
	pw.writeLine(sep)

	# save current page
	pw.savePage()

	# print the field headers section title
	pw.writeLine("Field Header Information:")

	# print a separator line
	pw.writeLine(sep)

	# print labels for field headers output
	pw.writeLine("%3s%13s%7s%8s%10s" % \
		  ("#", "Field name", "Type", "Length", "Decimals"))

	# print a separator line
	pw.writeLine(sep)

	# get num. of fields from file header
	num_flds = file_header["num_flds"]

	# get the field headers
	field_headers = dr.read_dbf_field_headers()

	# extract individual lists from the field headers list of lists
	fld_nam, fld_typ, fld_len, fld_dec = (
		field_headers[0], 
		field_headers[1], 
		field_headers[2], 
		field_headers[3] )

	fld_num = 0

	# print the field headers metadata
	while (fld_num < num_flds):
		s1 =  "%3s" % (fld_num + 1) 
		s2 =  "%13s" % (fld_nam[fld_num])
		#s3 =  "%4s" % (string.replace(fld_typ[fld_num], '\0', ' ')) ,
		s3 =  "%4s" % (fld_typ[fld_num])
		s4 =  "%5s" % (fld_len[fld_num])
		s5 =  "%7s" % (fld_dec[fld_num])

		s = s1 + " " + s2 + " " + s3 + " " + s4 + " " + s5
		pw.writeLine(s)
		fld_num = fld_num + 1
		
	# print a separator line
	pw.writeLine(sep)

	# save current page
	pw.savePage()

	# position the DBFReader instance to start reading data records
	dr.reset()

	# print the data records section title
	pw.writeLine("DBF Data Records:")

	# print a separator line
	pw.writeLine(sep)
	
	# print the data records
	rec_num = 0
	while (dr.has_next_record()):
		rec_num = rec_num + 1
		# get next data record from the DBFReader
		r = dr.next_record()
		# convert it from a list to a human-friendly string
		fr = dbf_record_to_string(r)
		# the serial num. of the record
		s1 = "%7d:" % (rec_num)
		# the record itself
		s2 = "%s" % (fr)
		# serial num. + record
		s = s1 + " " + s2
		# print the record
		pw.writeLine(s)

	# print a separator line
	pw.writeLine(sep)

	# save current page
	pw.savePage()

	# close the DBFReader
	dr.close()

	# close the PDFWriter
	pw.close()

##------------------------ Global code -----------------------------------

def dbf_record_to_string(r):

	'''
	Convert a dbf record returned by DBFReader.next_record() to a string
	format suitable for display.
	The value returned by next_record() is a tuple which contains
	two items:
	- 1: the deleted flag, value either " " (not deleted) or "D" (deleted)
	- 2: a list containing all the fields of the record, as strings
	Extract both the above items and convert everything into one single
	string for purpose of display. Put a pipe character ("|") before
	first field, between fields, and after last field, to delimit them.
	'''

	del_flag = r[0]
	str_rec = del_flag + "|"
	rec = r[1]
	for item in rec:
		str_rec = str_rec + item + "|"

	return str_rec

##------------------------ Global code -----------------------------------

# invoke main

if __name__ == '__main__':
	main()

##------------------------ EOF - DBFReader.py ----------------------------
